#include <arpa/inet.h>
#include <iostream>
#include <netinet/in.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <unistd.h>

class client {
public:
    static client &GetInstance(std::string server_ip, const int server_port) {
        static client cli(std::move(server_ip), server_port);
        return cli;
    }

    bool initiate_connection() {
        _sock_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock_fd < 0) {
            std::cerr << "socket error" << std::endl;
            return false;
        }

        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(_server_port);
        if (inet_pton(AF_INET, _server_ip.c_str(), &server_addr.sin_addr) <= 0) {
            std::cerr << "Invalid address/ Address not supported" << std::endl;
            return false;
        }

        if (connect(_sock_fd, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
            std::cerr << "Connection failed" << std::endl;
            return false;
        }

        std::cout << "Connect to server " << _server_ip << ":" << _server_port << std::endl;
        return true;
    }

    void run() {
        while (true) {
            std::cout << "输入：";
            std::string str{};
            std::getline(std::cin, str);
            if (str == "quit") {
                std::cout << "client quit,Bye!" << std::endl;
                break;
            }
            send(_sock_fd, str.c_str(), str.size(), 0);

            char buffer[1024];
            int byte_received = recv(_sock_fd, buffer, sizeof(buffer) - 1, 0);
            if (byte_received <= 0) {
                if (byte_received == -1) {
                    std::cerr << "recv failed" << std::endl;
                } else {
                    std::cout << "server shutdown,Bye!" << std::endl;
                    return;
                }
            } else {
                buffer[byte_received] = '\0';
                std::cout << "echo: " << buffer << std::endl;
            }
        }
    }

private:
    client(std::string server_ip, const int server_port) : _server_ip(std::move(server_ip)),
                                                           _server_port(server_port) {}

    client(const client &c) = delete;

    client &operator=(const client &c) = delete;

    ~client() {
        close(_sock_fd);
    }

private:
    std::string _server_ip;
    int _server_port;
    int _sock_fd;
};

int main(int argc, char **argv) {
    if (argc != 3) {
        std::cout << "argc error\n";
        std::cout << "Usage : ./client server_ip server_port" << std::endl;

        std::cout << "argc = " << argc << std::endl;
        for (int i = 0; i < argc; ++i) {
            std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
        }
    }

    client &c = client::GetInstance((argv[1]), std::stoi(argv[2]));

    if (c.initiate_connection()) {
        c.run();
    }

    return 0;
}